{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1,   300] loss: 0.869\n",
      "[1,   600] loss: 0.175\n",
      "[1,   900] loss: 0.120\n",
      "accuracy on test set: 97 % \n",
      "[2,   300] loss: 0.095\n",
      "[2,   600] loss: 0.090\n",
      "[2,   900] loss: 0.086\n",
      "accuracy on test set: 97 % \n",
      "[3,   300] loss: 0.071\n",
      "[3,   600] loss: 0.070\n",
      "[3,   900] loss: 0.069\n",
      "accuracy on test set: 98 % \n",
      "[4,   300] loss: 0.055\n",
      "[4,   600] loss: 0.060\n",
      "[4,   900] loss: 0.059\n",
      "accuracy on test set: 98 % \n",
      "[5,   300] loss: 0.051\n",
      "[5,   600] loss: 0.052\n",
      "[5,   900] loss: 0.048\n",
      "accuracy on test set: 98 % \n",
      "[6,   300] loss: 0.040\n",
      "[6,   600] loss: 0.044\n",
      "[6,   900] loss: 0.047\n",
      "accuracy on test set: 98 % \n",
      "[7,   300] loss: 0.034\n",
      "[7,   600] loss: 0.045\n",
      "[7,   900] loss: 0.042\n",
      "accuracy on test set: 97 % \n",
      "[8,   300] loss: 0.031\n",
      "[8,   600] loss: 0.036\n",
      "[8,   900] loss: 0.036\n",
      "accuracy on test set: 98 % \n",
      "[9,   300] loss: 0.031\n",
      "[9,   600] loss: 0.034\n",
      "[9,   900] loss: 0.031\n",
      "accuracy on test set: 98 % \n",
      "[10,   300] loss: 0.029\n",
      "[10,   600] loss: 0.029\n",
      "[10,   900] loss: 0.031\n",
      "accuracy on test set: 98 % \n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "from torchvision import transforms\n",
    "from torchvision import datasets\n",
    "from torch.utils.data import DataLoader\n",
    "import torch.nn.functional as F\n",
    "import torch.optim as optim\n",
    "\n",
    "# prepare dataset\n",
    "\n",
    "batch_size = 64\n",
    "transform = transforms.Compose(\n",
    "    [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])  # 归一化,均值和方差\n",
    "\n",
    "train_dataset = datasets.MNIST(\n",
    "    root='../dataset/mnist/', train=True, download=True, transform=transform)\n",
    "train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)\n",
    "test_dataset = datasets.MNIST(\n",
    "    root='../dataset/mnist/', train=False, download=True, transform=transform)\n",
    "test_loader = DataLoader(test_dataset, shuffle=False, batch_size=batch_size)\n",
    "\n",
    "# design model using class\n",
    "\n",
    "\n",
    "class Net(torch.nn.Module):\n",
    "    def __init__(self):\n",
    "        super(Net, self).__init__()\n",
    "        # 卷积层\n",
    "        self.conv1 = torch.nn.Conv2d(1, 5, padding=1, kernel_size=3)\n",
    "        self.conv2 = torch.nn.Conv2d(5, 10, kernel_size=3)\n",
    "        self.conv3 = torch.nn.Conv2d(10, 15, kernel_size=3)\n",
    "        # 池化层\n",
    "        self.pooling = torch.nn.MaxPool2d(2)\n",
    "        # 全连接层\n",
    "        self.l1 = torch.nn.Linear(60, 1024)\n",
    "        self.l2 = torch.nn.Linear(1024, 512)\n",
    "        self.l5 = torch.nn.Linear(512, 10)\n",
    "\n",
    "    def forward(self, x):\n",
    "        conv_size = x.size(0)\n",
    "        x = F.relu(self.pooling(self.conv1(x)))\n",
    "        x = F.relu(self.pooling(self.conv2(x)))\n",
    "        x = F.relu(self.pooling(self.conv3(x)))\n",
    "        x = x.view(conv_size, -1)\n",
    "        x = F.relu(self.l1(x))\n",
    "        x = F.relu(self.l2(x))\n",
    "        x = self.l5(x)\n",
    "        return x  # 最后一层不做激活，不进行非线性变换\n",
    "\n",
    "\n",
    "model = Net()\n",
    "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n",
    "model.to(device)\n",
    "# construct loss and optimizer\n",
    "criterion = torch.nn.CrossEntropyLoss()\n",
    "\n",
    "optimizer = optim.SGD(model.parameters(), lr=0.1, momentum=0.5)\n",
    "\n",
    "# training cycle forward, backward, update\n",
    "\n",
    "\n",
    "def train(epoch):\n",
    "    running_loss = 0.0\n",
    "    for batch_idx, data in enumerate(train_loader, 0):\n",
    "        # 获得一个批次的数据和标签\n",
    "        inputs, target = data\n",
    "        inputs, target = inputs.to(device), target.to(device)\n",
    "        optimizer.zero_grad()\n",
    "        outputs = model(inputs)\n",
    "        loss = criterion(outputs, target)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "\n",
    "        running_loss += loss.item()\n",
    "        if batch_idx % 300 == 299:\n",
    "            print('[%d, %5d] loss: %.3f' %\n",
    "                  (epoch+1, batch_idx+1, running_loss/300))\n",
    "            running_loss = 0.0\n",
    "\n",
    "\n",
    "def test():\n",
    "    correct = 0\n",
    "    total = 0\n",
    "    with torch.no_grad():\n",
    "        for data in test_loader:\n",
    "            images, labels = data\n",
    "            images, labels = images.to(device), labels.to(device)\n",
    "            outputs = model(images)\n",
    "            # dim = 1 列是第0个维度，行是第1个维度\n",
    "            _, predicted = torch.max(outputs.data, dim=1)\n",
    "            # print(\"predicted = \",predicted,\"\\tlabel = \",labels)\n",
    "            total += labels.size(0)\n",
    "            correct += (predicted == labels).sum().item()  # 张量之间的比较运算\n",
    "    print('accuracy on test set: %d %% ' % (100*correct/total))\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    for epoch in range(10):\n",
    "        train(epoch)\n",
    "        test()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**ps:计算output维度方式，预先计算一次看output.shape的值**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([1, 10, 98, 98])\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "in_channels ,out_channels = 1, 10       #输入维度   输出维度\n",
    "width, height = 28, 28                #图片宽度   图片高度\n",
    "my_1_kernel_size = 3    #卷积核大小\n",
    "batch_size = 64 \n",
    "\n",
    "input = torch.randn(batch_size, in_channels, width, height)\n",
    "conv_layer = torch.nn.Conv2d(in_channels, out_channels, kernel_size = my_1_kernel_size)\n",
    "output = conv_layer(input)\n",
    "\n",
    "print(output.shape) #输出参数： batch_size  ,输出维度   ，输出宽度  ，输出高度  \n",
    "#下一个全连接层输入长度即：输出维度 * 输出宽度 * 输出高度\n"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "44143c5ac5f3ceb8e37c69c3af73325ae55d21292b2c7b54871fd886482dde4c"
  },
  "kernelspec": {
   "display_name": "Python 3.6.13 ('pytorch')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.13"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
